package com.huiyeji.dynamic.core;

import com.huiyeji.dynamic.core.exception.DynamicForcedTerminationException;
import com.huiyeji.dynamic.core.exception.DynamicObjectInvokeException;
import com.huiyeji.dynamic.core.exception.DynamicStatementExecuteException;
import com.huiyeji.dynamic.core.handler.PrefixHandler;
import com.huiyeji.dynamic.core.provider.ParamProvider;

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;

/**
 * @author Huiyeji
 */
public class DefaultDynamicStatementExecutor implements DynamicExecutor{

    @Override
    public Object execute(DynamicContext context ,DynamicStatement statement) {

        if (statement == null){
            return null;
        }

        if (context == null){
            throw new DynamicStatementExecuteException("dynamic object context not found !");
        }

        Collection<DynamicInterceptor> interceptors = context.getInterceptors();
        for (DynamicInterceptor interceptor : interceptors) {
            interceptor.preHandle(context,statement);
        }

        if (!statement.isRequireExecute()){
            return null;
        }

        DynamicObjectFactory factory = context.getFactory();

        if (factory == null){
            throw new DynamicStatementExecuteException("dynamic object factory not found !");
        }

        try {

            String dynamicObjectName = statement.getDynamicObjectName();
            DynamicObject dynamicObject = factory.getDynamicObject(dynamicObjectName);
            if (dynamicObject == null){
                dynamicObject = context.getPluginManager().getPlugin(dynamicObjectName);
                if (dynamicObject == null){
                    throw new DynamicStatementExecuteException("Dynamic Object 【"+dynamicObjectName+ "】 Not Found !");
                }
            }
            List<ParamProvider> paramProviders = statement.getParamProviders();
            Object[] args = new Object[paramProviders.size()];
            for (int i = 0; i < paramProviders.size(); i++) {
                args[i] = paramProviders.get(i).get(context);
            }

            Object returnValue;

            try{
                returnValue = dynamicObject.invoke(statement.getDynamicMethodName(), args);
            }catch (InvocationTargetException exception){
                Throwable cause = exception.getCause();
                if (cause instanceof DynamicForcedTerminationException){
                    throw (DynamicForcedTerminationException) cause;
                }
                throw new DynamicObjectInvokeException(dynamicObject,statement.getDynamicMethodName(),cause);
            }

            if (returnValue instanceof DynamicObject){
                DynamicObject dynamicResultObject = (DynamicObject) returnValue;
                if (statement.hasReturnValue()){
                    dynamicResultObject.setDynamicObjectName(statement.getDynamicResultName());
                }
                factory.registerDynamicObject(dynamicResultObject);
            } else {
                if (statement.hasReturnValue()){
                    context.setObject(statement.getDynamicResultName(),returnValue);
                }
            }

            PrefixHandler prefixHandler = statement.getPrefixHandler();
            if (prefixHandler != null){
                prefixHandler.postHandle(context);
            }

            for (DynamicInterceptor interceptor : interceptors) {
                interceptor.postHandle(context,statement);
            }

            return returnValue;
        } catch (DynamicForcedTerminationException exception){
            throw exception;
        } catch (Exception exception){
            throw new DynamicStatementExecuteException(exception.getMessage() + "\n    at 【 "+statement.getOriginalStatement()+" 】" );
        }
    }
}
